package com.pearl.study05.demo.controller;

import cn.hutool.core.collection.CollUtil;
import cn.hutool.core.io.FileUtil;
import cn.hutool.core.util.ObjectUtil;
import com.pearl.study05.demo.common.R;
import com.pearl.study05.demo.vo.ProcessDefinitionVO;
import io.swagger.v3.oas.annotations.Operation;
import io.swagger.v3.oas.annotations.tags.Tag;
import org.camunda.bpm.engine.AuthorizationException;
import org.camunda.bpm.engine.RepositoryService;
import org.camunda.bpm.engine.exception.NotFoundException;
import org.camunda.bpm.engine.impl.util.IoUtil;
import org.camunda.bpm.engine.repository.Deployment;
import org.camunda.bpm.engine.repository.DeploymentBuilder;
import org.camunda.bpm.engine.repository.ProcessDefinition;
import org.camunda.bpm.engine.repository.ProcessDefinitionQuery;
import org.camunda.bpm.engine.rest.dto.repository.ProcessDefinitionDiagramDto;
import org.camunda.bpm.engine.rest.exception.InvalidRequestException;
import org.camunda.bpm.engine.rest.exception.RestException;
import org.camunda.bpm.engine.rest.util.URLEncodingUtil;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.*;
import org.springframework.web.multipart.MultipartFile;

import javax.ws.rs.core.Response;
import java.io.File;
import java.io.IOException;
import java.io.InputStream;
import java.io.UnsupportedEncodingException;
import java.util.ArrayList;
import java.util.List;

/**
 * @author TD
 * @version 1.0
 * @date 2023/7/31
 */
@RequestMapping("/repository")
@RestController
@Tag(name = "仓库服务", description = "仓库服务")
public class RepositoryController {

    @Autowired
    private RepositoryService repositoryService;

    @PostMapping("/deploy")
    @Operation(summary = "BPMN文件部署流程")
    public R<String> deploy(@RequestParam MultipartFile file) throws IOException {
        // 1. 构建部署对象
        DeploymentBuilder builder = repositoryService.createDeployment();
        builder.name("测试部署");
        InputStream inputStream = file.getInputStream();
        builder.addInputStream(file.getOriginalFilename(), inputStream);
        // 2.部署并返回ID
        Deployment deployment = builder.deploy();
        inputStream.close();
        return R.success(deployment.getId());
    }

    @DeleteMapping("/deleteDeployment")
    @Operation(summary = "删除部署")
    public R<String> deleteDeployment(String deploymentId, boolean cascade, boolean skipCustomListeners, boolean skipIoMappings) {
        repositoryService.deleteDeployment(deploymentId, cascade, skipCustomListeners, skipCustomListeners);
        return R.success();
    }

    @DeleteMapping("/deleteProcessDefinition")
    @Operation(summary = "根据ID删除流程定义")
    public R<String> deleteProcessDefinition(String processDefinitionId, boolean cascade, boolean skipCustomListeners, boolean skipIoMappings) {
        repositoryService.deleteProcessDefinition(processDefinitionId, cascade, skipCustomListeners, skipCustomListeners);
        return R.success();
    }

    @DeleteMapping("/deleteProcessDefinitionByKey")
    @Operation(summary = "根据Key删除流程定义")
    public R<String> deleteProcessDefinitionByKey(String key, boolean cascade, boolean skipCustomListeners, boolean skipIoMappings) {
        repositoryService.deleteProcessDefinitions()
                .byKey(key)
                .delete();
        return R.success();
    }

    @DeleteMapping("/deleteProcessDefinitionByIds")
    @Operation(summary = "根据ID集合删除流程定义")
    public R<String> deleteProcessDefinitionByIds(@RequestBody List<String> ids) {
        repositoryService.deleteProcessDefinitions()
                .byIds(ids.toArray(String[]::new)).delete();
        return R.success();
    }

    @GetMapping("/getProcessDefinitionById")
    @Operation(summary = "根据ID查询流程定义")
    public R<ProcessDefinitionVO> getProcessDefinitionById(@RequestParam String processDefinitionId) {
        ProcessDefinition processDefinition = repositoryService.getProcessDefinition(processDefinitionId);
        ProcessDefinitionVO vo = new ProcessDefinitionVO();
        vo.setKey(processDefinition.getKey());
        vo.setVersion(processDefinition.getVersion());
        vo.setHistoryLevel(processDefinition.getHistoryTimeToLive());
        vo.setHistoryTimeToLive(processDefinition.getHistoryTimeToLive());
        vo.setVersion(processDefinition.getVersion());
        return R.success(vo);
    }

    @GetMapping("/query")
    @Operation(summary = "条件查询流程定义")
    public R<List<ProcessDefinitionVO>> query(String name) {
        // 1. 定义条件
        ProcessDefinitionQuery query = repositoryService.createProcessDefinitionQuery();
        query.latestVersion();// 最后一个版本
        List<ProcessDefinition> list = query.list();
        // 2. 对象转换
        List<ProcessDefinitionVO> result = new ArrayList<>();
        if (CollUtil.isNotEmpty(list)) {
            for (ProcessDefinition processDefinition : list) {
                ProcessDefinitionVO vo = new ProcessDefinitionVO();
                vo.setKey(processDefinition.getKey());
                vo.setVersion(processDefinition.getVersion());
                vo.setHistoryLevel(processDefinition.getHistoryTimeToLive());
                vo.setHistoryTimeToLive(processDefinition.getHistoryTimeToLive());
                vo.setVersion(processDefinition.getVersion());
                result.add(vo);
            }
        }
        return R.success(result);
    }

    @PostMapping("/suspendProcessDefinitionByKey")
    @Operation(summary = "根据KEY挂起流程定义")
    public R<?> suspendProcessDefinitionByKey(@RequestParam String processDefinitionKey) {
        repositoryService.suspendProcessDefinitionByKey(processDefinitionKey);
        return R.success();
    }

    @PostMapping("/activateProcessDefinitionByKey")
    @Operation(summary = "根据KEY激活流程定义")
    public R<?> activateProcessDefinitionByKey(@RequestParam String processDefinitionKey) {
        repositoryService.activateProcessDefinitionByKey(processDefinitionKey);
        return R.success();
    }

    @PostMapping("/getProcessModelFile")
    @Operation(summary = "下载流程模型文件")
    public R<?> getProcessModel(@RequestParam String processDefinitionId) {
        // 查询流程定义
        ProcessDefinition processDefinition = repositoryService.getProcessDefinition(processDefinitionId);
        if (ObjectUtil.isNotNull(processDefinition)) {
            // 获取二进制流
            InputStream inputStream = repositoryService.getProcessModel(processDefinitionId);
            // 保存到文件
            File tempFile = FileUtil.createTempFile();
            FileUtil.writeFromStream(inputStream, tempFile, true);
        }
        return R.success();
    }

    @PostMapping("/getProcessModeLXml")
    @Operation(summary = "获取流程模型XML文件内容")
    public R<ProcessDefinitionDiagramDto> getProcessModeLXml(@RequestParam String processDefinitionId) {
        InputStream processModelIn = null;
        try {
            processModelIn = repositoryService.getProcessModel(processDefinitionId);
            byte[] processModel = IoUtil.readInputStream(processModelIn, "processModelBpmn20Xml");
            ProcessDefinitionDiagramDto dto = ProcessDefinitionDiagramDto.create(processDefinitionId, new String(processModel, "UTF-8"));
            return R.success(dto);
        } catch (AuthorizationException e) {
            throw e;
        } catch (NotFoundException e) {
            throw new InvalidRequestException(Response.Status.NOT_FOUND, e, "No matching definition with id " + processDefinitionId);
        } catch (UnsupportedEncodingException e) {
            throw new RestException(Response.Status.INTERNAL_SERVER_ERROR, e);
        } finally {
            IoUtil.closeSilently(processModelIn);
        }
    }

    @PostMapping("/getProcessDiagram")
    @Operation(summary = "下载流程模型图片")
    public R<?> getProcessDiagram(@RequestParam String processDefinitionId) {
        // 查询流程定义
        ProcessDefinition processDefinition = repositoryService.getProcessDefinition(processDefinitionId);
        if (ObjectUtil.isNotNull(processDefinition)) {
            // 获取二进制流
            InputStream inputStream = repositoryService.getProcessDiagram(processDefinitionId);
            // 保存到文件
            File tempFile = FileUtil.createTempFile();
            FileUtil.writeFromStream(inputStream, tempFile, true);
        }
        return R.success();
    }
}
